#!/usr/bin/env bash
#

# path to lua-cross-compiler
DIR=`realpath $0`
DIR=`dirname $DIR`

CC=@CROSS_TRIPLE@-gcc
LLVM_LUAC="./@CROSS_TRIPLE@-llvm-luac"

# find llvm-luac cross-compiler
if [[ ! -x "$LLVM_LUAC" ]]; then
	LLVM_LUAC="$DIR/@CROSS_TRIPLE@-llvm-luac"
fi
if [[ ! -x "$LLVM_LUAC" ]]; then
	LLVM_LUAC=`which @CROSS_TRIPLE@-llvm-luac`
fi

ARCH=@CROSS_ARCH@
CPU=@CROSS_CPU@
MODULE="0"
NO_ASSEMBLE="0"
FILE=lua-native-out
FILES=""
DEBUG="0"
KEEP_TMPS="0"
MODE="asm"
EXTRA_ARGS=

function version() {
	$LLVM_LUAC -version
	exit 0;
}

function usage() {
	echo "USAGE: $0 [options] <script>

OPTIONS:
  -lua-module      - Compile Lua script into a loadable module instead of a standalone
                       executable.
  -no-assemble     - Only compile to native Assembly code or C-code, don't compile to machine code.
  -debug           - Turns off all optimizations and turns on debug info.  Both Lua debug
                       info and gcc debug symbols are enabled.
  -keep-tmps       - Don't delete temp. files generated by intermediate stages.  Use only
                       for debuging generated code or if you are really curious!
  -mode=<mode>     - Switch how LLVM bitcode is generated and compiled into native code.
                   -   asm - compiles Lua scripts into asm-code.
                   -   cbe - compiles Lua scripts into C-code. (only for testing, don't use it.)
  -******          - All other options passed to 'llvm-luac'.  See below for a list of
                       options supported by 'llvm-luac'.

llvm-luac '-help' output:
"
	$LLVM_LUAC -help
	exit 0;
}

# parse command line parameters.
CONSUME=""
for arg in "$@" ; do
	case "$CONSUME" in
	-o)  OUTPUT_FILE=` echo "$arg" | sed -e 's/-o=//'` ;;
	-L)  EXTRA_ARGS="$EXTRA_ARGS -L $arg" ;;
	esac
	if [[ ! -z "$CONSUME" ]]; then
		CONSUME=""
		continue
	fi
	case "$arg" in
	-lua-mod*)  MODULE="1"; EXTRA_ARGS="$EXTRA_ARGS $arg" ;;
	-debug)  DEBUG="1" ;;
	-no-assemble)  NO_ASSEMBLE="1" ;;
	-keep-tmps)  KEEP_TMPS="1" ;;
	-mode=*)  MODE=` echo "$arg" | sed -e 's/-mode=//'` ;;
	-help|--help|-h)  usage ;;
	-version|--version|-v)  version ;;
	-o|-L)  CONSUME="$arg" ;;
	-L*)  EXTRA_ARGS="$EXTRA_ARGS $arg" ;;
	-*)  EXTRA_ARGS="$EXTRA_ARGS $arg" ;;
	*) FILE=${arg/.lua/}; FILES="$FILES $arg" ;;
	esac
done

# find the mode's output file extension.
OUTPUT_EXT=""
MODE_OUT=""
case "$MODE" in
	cbe) OUTPUT_EXT=".c" ;;
	asm) OUTPUT_EXT=".s" ;;
esac
if [[ -z "$OUTPUT_FILE" ]]; then
	OUTPUT_FILE="${FILE}${OUTPUT_EXT}"
else
	FILE=${OUTPUT_FILE}
fi
MODE_OUT="${FILE}${OUTPUT_EXT}"

# select debug/optimize parameters.
if [[ $DEBUG == "1" ]]; then
	LUA_FLAGS=" -O0 -g "
	OPT_FLAGS=" -disable-opt "
	LLC_FLAGS=" "
else
	LUA_FLAGS=" -O3 -s "
	OPT_FLAGS=" -O3 -std-compile-opts -tailcallelim -tailduplicate "
	LLC_FLAGS=" -tailcallopt "
fi
LLC_FLAGS=" -mcpu=$CPU $LLC_FLAGS "

#
# run llvm-luac to compile Lua source/bytecode to LLVM bitcode.
#
echo "$LLVM_LUAC $EXTRA_ARGS $LUA_FLAGS -bc -o ${FILE}.bc ${FILES}"
$LLVM_LUAC $EXTRA_ARGS $LUA_FLAGS -bc -o ${FILE}.bc ${FILES} || {
	echo "llvm-luac: failed to compile Lua code into LLVM bitcode."
	exit 1;
}
TMPS="${FILE}.bc"

# use one of the compile modes.
case "$MODE" in
	cbe)
		TMPS="$TMPS ${FILE}_opt.bc"
		echo "opt $OPT_FLAGS -f -o ${FILE}_opt.bc ${FILE}.bc"
		opt $OPT_FLAGS -f -o ${FILE}_opt.bc ${FILE}.bc
		echo "llc $LLC_FLAGS -f -march=c -o ${FILE}.c ${FILE}_opt.bc"
		llc $LLC_FLAGS -f -march=c -o ${MODE_OUT} ${FILE}_opt.bc
		;;
	asm)
		TMPS="$TMPS ${FILE}_opt.bc"
		echo "opt $OPT_FLAGS -f -o ${FILE}_opt.bc ${FILE}.bc"
		opt $OPT_FLAGS -f -o ${FILE}_opt.bc ${FILE}.bc
		echo "llc -march=$ARCH $LLC_FLAGS -f -filetype=asm -o ${FILE}.s ${FILE}_opt.bc"
		llc -march=$ARCH $LLC_FLAGS -f -filetype=asm -o ${MODE_OUT} ${FILE}_opt.bc
		;;
	*)
		echo "Invalid compile mode: $MODE"
		;;
esac

# compile to stand-alone
if [[ $MODULE == "0" && $NO_ASSEMBLE == "0" ]]; then
	echo "${CC} -o ${OUTPUT_FILE} ${MODE_OUT} -lm -ldl"
	${CC} -o ${OUTPUT_FILE} ${MODE_OUT} -lm -ldl && \
	TMPS="$TMPS ${MODE_OUT}"
fi

# keep assembly/c-code output.
if [[ $NO_ASSEMBLE == "1" && $MODE_OUT != $OUTPUT_FILE ]]; then
	echo "1 no-asm=$NO_ASSEMBLE, mode_out=$MODE_OUT, out=$OUTPUT_FILE"
	echo "mv $MODE_OUT $OUTPUT_FILE"
	mv $MODE_OUT $OUTPUT_FILE
else
	echo "2 no-asm=$NO_ASSEMBLE, mode_out=$MODE_OUT, out=$OUTPUT_FILE"
fi

if [[ $KEEP_TMPS == "0" ]]; then
	rm -f $TMPS
fi

